Latviešu

Padziļināts React `useDeferredValue` huka apskats. Uzziniet, kā novērst UI aizturi, izprast vienlaicīgumu, salīdzināt ar `useTransition` un veidot ātrākas lietotnes globālai auditorijai.

React `useDeferredValue`: Visaptverošs ceļvedis nebloķējošai lietotāja saskarnes veiktspējai

Mūsdienu tīmekļa izstrādes pasaulē lietotāja pieredze ir vissvarīgākā. Ātra, atsaucīga saskarne vairs nav greznība — tā ir prasība. Lietotājiem visā pasaulē, kas izmanto visdažādākās ierīces un tīkla apstākļus, lēna, raustīta lietotāja saskarne (UI) var būt atšķirība starp atgriezušos klientu un zaudētu klientu. Šeit spēli maina React 18 vienlaicīguma (concurrency) funkcijas, īpaši useDeferredValue huks.

Ja esat kādreiz veidojis React lietotni ar meklēšanas lauku, kas filtrē lielu sarakstu, datu režģi, kas atjaunojas reāllaikā, vai sarežģītu informācijas paneli, jūs, visticamāk, esat saskāries ar bēdīgi slaveno UI sasalšanu. Lietotājs raksta, un uz sekundi visa lietotne nereaģē. Tas notiek tāpēc, ka tradicionālā renderēšana React ir bloķējoša. Stāvokļa atjauninājums izraisa atkārtotu renderēšanu, un nekas cits nevar notikt, kamēr tā nav pabeigta.

Šis visaptverošais ceļvedis jūs aizvedīs padziļinātā useDeferredValue huka izpētē. Mēs izpētīsim problēmu, ko tas atrisina, kā tas darbojas zem pārsega ar React jauno vienlaicīguma dzinēju un kā jūs varat to izmantot, lai veidotu neticami atsaucīgas lietotnes, kas šķiet ātras, pat ja tās veic daudz darba. Mēs apskatīsim praktiskus piemērus, progresīvus modeļus un svarīgākās labākās prakses globālai auditorijai.

Pamatproblēmas izpratne: bloķējošā lietotāja saskarne

Pirms mēs varam novērtēt risinājumu, mums ir pilnībā jāizprot problēma. React versijās, kas bija pirms 18. versijas, renderēšana bija sinhrona un nepārtraucama. Iedomājieties vienas joslas ceļu: kad automašīna (renderēšana) iebrauc, neviena cita automašīna nevar pabraukt garām, kamēr tā nav sasniegusi galu. Tā strādāja React.

Apskatīsim klasisku scenāriju: meklējams produktu saraksts. Lietotājs raksta meklēšanas lodziņā, un zem tā esošais tūkstošiem vienību saraksts tiek filtrēts, pamatojoties uz viņu ievadi.

Tipiska (un lēna) implementācija

Lūk, kā kods varētu izskatīties pirms-React 18 pasaulē vai neizmantojot vienlaicīguma funkcijas:

Komponenta struktūra:

Fails: SearchPage.js

import React, { useState } from 'react'; import ProductList from './ProductList'; import { generateProducts } from './data'; // funkcija, kas izveido lielu masīvu const allProducts = generateProducts(20000); // Iedomāsimies 20 000 produktu function SearchPage() { const [query, setQuery] = useState(''); const filteredProducts = allProducts.filter(product => { return product.name.toLowerCase().includes(query.toLowerCase()); }); function handleChange(e) { setQuery(e.target.value); } return (

); } export default SearchPage;

Kāpēc tas ir lēns?

Izsekosim lietotāja darbībai:

  1. Lietotājs ieraksta burtu, piemēram, 'a'.
  2. Tiek aktivizēts onChange notikums, izsaucot handleChange.
  3. Tiek izsaukts setQuery('a'). Tas ieplāno SearchPage komponenta atkārtotu renderēšanu.
  4. React sāk atkārtotu renderēšanu.
  5. Renderēšanas laikā tiek izpildīta rinda const filteredProducts = allProducts.filter(...). Šī ir dārgā daļa. 20 000 vienību masīva filtrēšana, pat ar vienkāršu 'includes' pārbaudi, prasa laiku.
  6. Kamēr notiek šī filtrēšana, pārlūkprogrammas galvenais pavediens (main thread) ir pilnībā aizņemts. Tas nevar apstrādāt jaunu lietotāja ievadi, nevar vizuāli atjaunināt ievades lauku un nevar palaist citu JavaScript kodu. UI ir bloķēts.
  7. Kad filtrēšana ir pabeigta, React turpina renderēt ProductList komponentu, kas pats par sevi var būt smaga operācija, ja tas renderē tūkstošiem DOM mezglu.
  8. Visbeidzot, pēc visa šī darba, DOM tiek atjaunināts. Lietotājs redz, ka ievades lodziņā parādās burts 'a', un saraksts tiek atjaunināts.

Ja lietotājs raksta ātri — piemēram, "apple" — viss šis bloķēšanas process notiek ar 'a', tad 'ap', tad 'app', 'appl' un 'apple'. Rezultāts ir pamanāma aizture, kur ievades lauks raustās un cenšas tikt līdzi lietotāja rakstīšanai. Tā ir slikta lietotāja pieredze, īpaši mazāk jaudīgās ierīcēs, kas ir izplatītas daudzās pasaules daļās.

Iepazīstinām ar React 18 vienlaicīgumu

React 18 fundamentāli maina šo paradigmu, ieviešot vienlaicīgumu (concurrency). Vienlaicīgums nav tas pats, kas paralēlisms (vairāku lietu darīšana vienlaicīgi). Tā vietā tā ir React spēja pauzēt, atsākt vai atmest renderēšanu. Vienas joslas ceļam tagad ir apdzīšanas joslas un satiksmes regulators.

Ar vienlaicīgumu React var iedalīt atjauninājumus divos veidos:

React tagad var sākt nesteidzīgu "pārejas" renderēšanu, un, ja pienāk steidzamāks atjauninājums (piemēram, cits taustiņsitiens), tas var apturēt ilgstošo renderēšanu, vispirms apstrādāt steidzamo un pēc tam atsākt savu darbu. Tas nodrošina, ka UI vienmēr paliek interaktīvs. useDeferredValue huks ir galvenais rīks, lai izmantotu šo jauno jaudu.

Kas ir `useDeferredValue`? Detalizēts paskaidrojums

Savā būtībā useDeferredValue ir huks, kas ļauj jums pateikt React, ka noteikta vērtība jūsu komponentā nav steidzama. Tas pieņem vērtību un atgriež jaunu šīs vērtības kopiju, kas "atpaliks", ja notiks steidzami atjauninājumi.

Sintakse

Šo huku ir neticami vienkārši lietot:

import { useDeferredValue } from 'react'; const deferredValue = useDeferredValue(value);

Tas arī viss. Jūs tam padodat vērtību, un tas jums atgriež šīs vērtības atlikto versiju.

Kā tas darbojas zem pārsega

Atklāsim šo maģiju. Kad jūs izmantojat useDeferredValue(query), lūk, ko dara React:

  1. Sākotnējā renderēšana: Pirmajā renderēšanas reizē deferredQuery būs tāds pats kā sākotnējais query.
  2. Notiek steidzams atjauninājums: Lietotājs ieraksta jaunu rakstzīmi. query stāvoklis mainās no 'a' uz 'ap'.
  3. Augstas prioritātes renderēšana: React nekavējoties izraisa atkārtotu renderēšanu. Šīs pirmās, steidzamās atkārtotās renderēšanas laikā useDeferredValue zina, ka notiek steidzams atjauninājums. Tāpēc tas joprojām atgriež iepriekšējo vērtību, 'a'. Jūsu komponents tiek ātri atkārtoti renderēts, jo ievades lauka vērtība kļūst par 'ap' (no stāvokļa), bet jūsu UI daļa, kas ir atkarīga no deferredQuery (lēnais saraksts), joprojām izmanto veco vērtību un nav jāpārrēķina. UI paliek atsaucīgs.
  4. Zemas prioritātes renderēšana: Uzreiz pēc steidzamās renderēšanas pabeigšanas React fonā sāk otru, nesteidzīgu atkārtotu renderēšanu. *Šajā* renderēšanā useDeferredValue atgriež jauno vērtību, 'ap'. Šī fona renderēšana ir tā, kas izraisa dārgo filtrēšanas operāciju.
  5. Pārtraucamība: Lūk, galvenā daļa. Ja lietotājs ieraksta citu burtu ('app'), kamēr zemās prioritātes renderēšana priekš 'ap' vēl notiek, React atmetīs šo fona renderēšanu un sāks no jauna. Tas piešķir prioritāti jaunajam steidzamajam atjauninājumam ('app') un pēc tam ieplāno jaunu fona renderēšanu ar jaunāko atlikto vērtību.

Tas nodrošina, ka dārgais darbs vienmēr tiek veikts ar visjaunākajiem datiem un tas nekad nebloķē lietotāju no jaunas ievades. Tas ir spēcīgs veids, kā samazināt smagu aprēķinu prioritāti bez sarežģītas manuālas "debouncing" vai "throttling" loģikas.

Praktiska implementācija: mūsu lēnās meklēšanas labošana

Pārveidosim mūsu iepriekšējo piemēru, izmantojot useDeferredValue, lai redzētu to darbībā.

Fails: SearchPage.js (Optimizēts)

import React, { useState, useDeferredValue, useMemo } from 'react'; import ProductList from './ProductList'; import { generateProducts } from './data'; const allProducts = generateProducts(20000); // Komponents saraksta attēlošanai, memoizēts veiktspējai const MemoizedProductList = React.memo(ProductList); function SearchPage() { const [query, setQuery] = useState(''); // 1. Atliekam vaicājuma vērtību. Šī vērtība atpaliks no 'query' stāvokļa. const deferredQuery = useDeferredValue(query); // 2. Dārgā filtrēšana tagad tiek vadīta ar deferredQuery. // Mēs to arī ietinam useMemo turpmākai optimizācijai. const filteredProducts = useMemo(() => { console.log('Filtrēšana priekš:', deferredQuery); return allProducts.filter(product => { return product.name.toLowerCase().includes(deferredQuery.toLowerCase()); }); }, [deferredQuery]); // Pārrēķina tikai tad, kad mainās deferredQuery function handleChange(e) { // Šis stāvokļa atjauninājums ir steidzams un tiks apstrādāts nekavējoties setQuery(e.target.value); } return (

{/* 3. Ievades lauks tiek kontrolēts ar augstas prioritātes 'query' stāvokli. Tas šķiet tūlītējs. */} {/* 4. Saraksts tiek renderēts, izmantojot atliktā, zemas prioritātes atjauninājuma rezultātu. */}
); } export default SearchPage;

Pārvērtības lietotāja pieredzē

Ar šo vienkāršo izmaiņu lietotāja pieredze tiek pārveidota:

Lietotne tagad šķiet ievērojami ātrāka un profesionālāka.

`useDeferredValue` pret `useTransition`: kāda ir atšķirība?

Šis ir viens no visbiežākajiem neskaidrību punktiem izstrādātājiem, kas mācās vienlaicīgo React. Gan useDeferredValue, gan useTransition tiek izmantoti, lai atzīmētu atjauninājumus kā nesteidzīgus, bet tie tiek lietoti dažādās situācijās.

Galvenā atšķirība ir: kur jums ir kontrole?

`useTransition`

Jūs izmantojat useTransition, kad jums ir kontrole pār kodu, kas izraisa stāvokļa atjauninājumu. Tas dod jums funkciju, parasti sauktu par startTransition, ar kuru ietīt jūsu stāvokļa atjauninājumu.

const [isPending, startTransition] = useTransition(); function handleChange(e) { const nextValue = e.target.value; // Steidzamo daļu atjauninām nekavējoties setInputValue(nextValue); // Lēno atjauninājumu ietinam startTransition startTransition(() => { setSearchQuery(nextValue); }); }

`useDeferredValue`

Jūs izmantojat useDeferredValue, kad jūs nekontrolējat kodu, kas atjaunina vērtību. Tas bieži notiek, kad vērtība nāk no `props`, no vecāka komponenta vai no cita huka, ko nodrošina trešās puses bibliotēka.

function SlowList({ valueFromParent }) { // Mēs nekontrolējam, kā valueFromParent tiek iestatīts. // Mēs to tikai saņemam un vēlamies atlikt renderēšanu, pamatojoties uz to. const deferredValue = useDeferredValue(valueFromParent); // ... izmantojam deferredValue, lai renderētu lēno komponenta daļu }

Salīdzinājuma kopsavilkums

Iezīme `useTransition` `useDeferredValue`
Ko tas ietin Stāvokļa atjaunināšanas funkciju (piem., startTransition(() => setState(...))) Vērtību (piem., useDeferredValue(myValue))
Kontroles punkts Kad jūs kontrolējat notikumu apstrādātāju vai atjauninājuma izraisītāju. Kad jūs saņemat vērtību (piem., no `props`) un jums nav kontroles pār tās avotu.
Ielādes stāvoklis Nodrošina iebūvētu `isPending` Būla vērtību. Nav iebūvēta karodziņa, bet to var atvasināt ar `const isStale = originalValue !== deferredValue;`.
Analoģija Jūs esat dispečers, kurš izlemj, kurš vilciens (stāvokļa atjauninājums) dodas pa lēno sliežu ceļu. Jūs esat stacijas priekšnieks, kurš redz, ka vērtība pienāk ar vilcienu, un nolemj to uz brīdi paturēt stacijā, pirms to parādīt uz galvenā tablo.

Padziļināti lietošanas gadījumi un modeļi

Papildus vienkāršai sarakstu filtrēšanai useDeferredValue paver vairākus spēcīgus modeļus sarežģītu lietotāja saskarņu veidošanai.

1. modelis: "novecojušas" saskarnes rādīšana kā atgriezeniskā saite

Lietotāja saskarne, kas atjaunojas ar nelielu aizkavi bez jebkādas vizuālas atgriezeniskās saites, lietotājam var šķist kļūdaina. Viņi varētu domāt, vai viņu ievade tika reģistrēta. Lielisks modelis ir nodrošināt smalku norādi, ka dati tiek atjaunināti.

To var panākt, salīdzinot sākotnējo vērtību ar atlikto vērtību. Ja tās atšķiras, tas nozīmē, ka gaida fona renderēšana.

function SearchPage() { const [query, setQuery] = useState(''); const deferredQuery = useDeferredValue(query); // Šī Būla vērtība mums norāda, vai saraksts atpaliek no ievades const isStale = query !== deferredQuery; const filteredProducts = useMemo(() => { // ... dārgā filtrēšana, izmantojot deferredQuery }, [deferredQuery]); return (

setQuery(e.target.value)} />
); }

Šajā piemērā, tiklīdz lietotājs sāk rakstīt, isStale kļūst par "true". Saraksts nedaudz nobāl, norādot, ka tas drīz tiks atjaunināts. Kad atliktā renderēšana ir pabeigta, query un deferredQuery atkal kļūst vienādi, isStale kļūst par "false", un saraksts atgriežas pilnā necaurredzamībā ar jaunajiem datiem. Tas ir ekvivalents isPending karodziņam no useTransition.

2. modelis: atjauninājumu atlikšana diagrammās un vizualizācijās

Iedomājieties sarežģītu datu vizualizāciju, piemēram, ģeogrāfisku karti vai finanšu diagrammu, kas tiek atkārtoti renderēta, pamatojoties uz lietotāja kontrolētu slīdni datumu diapazonam. Slīdņa vilkšana var būt ārkārtīgi raustīga, ja diagramma tiek atkārtoti renderēta katrā kustības pikselī.

Atliekot slīdņa vērtību, jūs varat nodrošināt, ka pats slīdņa rokturis paliek gluds un atsaucīgs, kamēr smagais diagrammas komponents graciozi tiek atkārtoti renderēts fonā.

function ChartDashboard() { const [year, setYear] = useState(2023); const deferredYear = useDeferredValue(year); // HeavyChart ir memoizēts komponents, kas veic dārgus aprēķinus // Tas tiks atkārtoti renderēts tikai tad, kad deferredYear vērtība nostabilizēsies. const chartData = useMemo(() => computeChartData(deferredYear), [deferredYear]); return (

setYear(parseInt(e.target.value, 10))} /> Izvēlētais gads: {year}
); }

Labākās prakses un biežākās kļūdas

Lai gan useDeferredValue ir spēcīgs rīks, tas jālieto apdomīgi. Šeit ir dažas galvenās labākās prakses, kas jāievēro:

Ietekme uz globālo lietotāja pieredzi (UX)

Tādu rīku kā useDeferredValue pieņemšana nav tikai tehniska optimizācija; tā ir apņemšanās nodrošināt labāku, iekļaujošāku lietotāja pieredzi globālai auditorijai.

Nobeigums

React useDeferredValue huks ir paradigmas maiņa veidā, kā mēs pieejam veiktspējas optimizācijai. Tā vietā, lai paļautos uz manuālām un bieži vien sarežģītām metodēm, piemēram, "debouncing" un "throttling", mēs tagad varam deklaratīvi pateikt React, kuras mūsu UI daļas ir mazāk kritiskas, ļaujot tam plānot renderēšanas darbu daudz inteliģentākā un lietotājam draudzīgākā veidā.

Izprotot vienlaicīguma pamatprincipus, zinot, kad lietot useDeferredValue pret useTransition, un piemērojot labākās prakses, piemēram, memoizāciju un lietotāja atgriezenisko saiti, jūs varat novērst UI raustīšanos un veidot lietotnes, kas ir ne tikai funkcionālas, bet arī patīkamas lietošanā. Konkurētspējīgā globālajā tirgū ātras, atsaucīgas un pieejamas lietotāja pieredzes nodrošināšana ir galvenā iezīme, un useDeferredValue ir viens no spēcīgākajiem rīkiem jūsu arsenālā, lai to sasniegtu.